package com.mages.debris.service;

import com.mages.debris.REST.DebrisRESTTemplate;
import com.mages.debris.util.CommonUtil;
import it.geosolutions.geoserver.rest.GeoServerRESTManager;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import it.geosolutions.geoserver.rest.encoder.datastore.GSGeoTIFFDatastoreEncoder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
import java.util.concurrent.*;

@Component
public class DebrisService {
    private String inputDEMURL;
    private String inputFilterURL;
    private String outputGeoserverURL;
    private String uuid;
    private String clientName;
    private String clientIP;
    private long req_timestamp;
    // TODO 发布时需修改以下配置
    private static String cacheDir;
    private static String user;
    private static String password;
    private static String workspace;
    private static String pythonInterpreter;
    private static String scriptPath;
    private static String logDir;
    private static String reliabilityTxt;
    private static String availabilityTxt;
    private static String jdbcUrl;
    private static double price;
    private static double reliability;
    private static double availability;
    private static String physicalName;
    private static String physicalIP;
    private static String redisHost;


//    initial
    public DebrisService() {
    }

    public DebrisService(String inputDEMURL,String inputFilterURL, String outputGeoserverURL) {
        this.inputDEMURL = inputDEMURL;
        this.inputFilterURL = inputFilterURL;
        this.outputGeoserverURL = outputGeoserverURL;
    }


    public DebrisService(String inputDEMURL, String inputFilterURL, String outputGeoserverURL, String uuid, String clientName, String clientIP,  long req_timestamp) {
        this.inputDEMURL = inputDEMURL;
        this.inputFilterURL = inputFilterURL;
        this.outputGeoserverURL = outputGeoserverURL;
        this.uuid = uuid;
        this.clientName = clientName;
        this.clientIP = clientIP;
        this.req_timestamp = req_timestamp;
    }



//set and get

    @Value("${PHYSICAL_NAME:DESKTOP-7CVV7V6}")
    public void setPhysicalName(String physicalName) {
        DebrisService.physicalName = physicalName;
    }

    @Value("${PHYSICAL_IP:192.168.101.69}")
    public void setPhysicalIP(String physicalIP) {
        DebrisService.physicalIP = physicalIP;
    }

    @Value("${REDIS_HOST:localhost}")
    public void setRedisHost(String redisHost) {
        DebrisService.redisHost = redisHost;
    }

    @Value("${QOS_PRICE:10}")
    public void setPrice(double price) {
        DebrisService.price = price;
    }

    @Value("${QOS_RELIABILITY:0.5}")
    public void setReliability(double reliability) {
        DebrisService.reliability = reliability;
    }

    @Value("${QOS_AVAILABILITY:1}")
    public void setAvailability(double availability) {
        DebrisService.availability = availability;
    }

    @Value("${SERVICE_CACHE:share/cache}")
    public void setCacheDir(String cacheDir) {
        DebrisService.cacheDir = cacheDir;
    }

    @Value("${GEOSERVER_USER:admin}")
    public void setUser(String user) {
        DebrisService.user = user;
    }

    @Value("${GEOSERVER_PASSWORD:geoserver}")
    public void setPassword(String password) {
        DebrisService.password = password;
    }

    @Value("${GEOSERVER_WORKSPACE:service_chain}")
    public void setWorkspace(String workspace) {
        DebrisService.workspace = workspace;
    }

    @Value("${SERVICE_PYTHON:E:/python/python.exe}")
    public void setPythonInterpreter(String pythonInterpreter) {
        DebrisService.pythonInterpreter = pythonInterpreter;
    }

    @Value("${SERVICE_SCRIPT:scripts/debris.py}")
    public void setScriptPath(String scriptPath) {
        DebrisService.scriptPath = scriptPath;
    }

    @Value("${SERVICE_LOG:share/log}")
    public void setLogDir(String logDir) {
        DebrisService.logDir = logDir;
    }

    @Value("${SERVICE_RELIABILITY:share/reliability.txt}")
    public void setReliabilityTxt(String reliabilityTxt) {
        DebrisService.reliabilityTxt = reliabilityTxt;
    }

    @Value("${SERVICE_AVAILABILITY:share/availability.txt}")
    public void setAvailability(String availabilityTxt) {
        DebrisService.availabilityTxt = availabilityTxt;
    }

    @Value("${SERVICE_JDBCURL:jdbc:sqlite:share/concurrency.db}")
    public void setJdbcUrl(String jdbcUrl) {
        DebrisService.jdbcUrl = jdbcUrl;
    }

    public String getOutputGeoserverURL() {
        return outputGeoserverURL;
    }

    public void setOutputGeoserverURL(String outputGeoserverURL) {
        this.outputGeoserverURL = outputGeoserverURL;
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getClientName() {
        return clientName;
    }

    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    public String getClientIP() {
        return clientIP;
    }

    public void setClientIP(String clientIP) {
        this.clientIP = clientIP;
    }

    public long getReq_timestamp() {
        return req_timestamp;
    }

    public void setReq_timestamp(long req_timestamp) {
        this.req_timestamp = req_timestamp;
    }


    //    创建数据的下载和存储目录
    @PostConstruct
    public void checkWorkdir(){
        // check whether dir/file exists
//        System.out.println("check workdir");
//        System.out.println(cacheDir);
//        System.out.println(logDir);
        // cache dir
        File file = new File(String.format("%s/download", cacheDir));
        if (!file.exists()) {
            boolean flag = file.mkdirs();
            if (!flag) {
                System.out.println("failed to make dir: cache/download");
                return;
            }
        }
        file = new File(String.format("%s/result", cacheDir));
        if (!file.exists()) {
            boolean flag = file.mkdirs();
            if (!flag) {
                System.out.println("failed to make dir: cache/result");
                return;
            }
        }
        // log dir
        file = new File(logDir);
        if (!file.exists()) {
            boolean flag = file.mkdirs();
            if (!flag) {
                System.out.println("failed to make dir: log");
            }
        }
        file = new File(reliabilityTxt);
        if (!file.exists()) {
            boolean flag = false;
            try {
                flag = file.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            if (!flag) {
                System.out.println("failed to make dir: reliabilityTxt");
            }
        }

        System.out.println(redisHost);
        System.out.println(physicalIP);

        // 创建Jedis客户端
        Jedis jedis = new Jedis(redisHost); // 默认端口为6379
        // 执行Redis命令
        jedis.set("debris_concurrency", "0");
        // 关闭连接
        jedis.close();

    }

    public String parseDataName(String dataURL) {
        int dataNameStartIdx = dataURL.indexOf("coverageId") + workspace.length() + 12;
        int dataNameEndIdx = dataURL.indexOf("format") - 1;
        return String.format("%s.tif", dataURL.substring(dataNameStartIdx, dataNameEndIdx));
    }

    public boolean downloadData(String dataURL, String dataPath, String auth) {
        try {
            File file = new File(dataPath);
            if (!file.exists()) {
                file.createNewFile();
            }
            URL url = new URL(dataURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(6000);
            conn.setReadTimeout(6000);
            conn.setRequestProperty("Authorization", String.format("Basic %s", auth));
            int code = conn.getResponseCode();
            if (code != HttpURLConnection.HTTP_OK) {
                throw new Exception("文件读取失败！");
            }
            DataInputStream in = new DataInputStream(conn.getInputStream());
            DataOutputStream out = new DataOutputStream(Files.newOutputStream(Paths.get(dataPath)));
            byte[] buffer = new byte[2048];
            int count = 0;
            while ((count = in.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

//    服务核心处理部分-----------------------------
    @Async
    public DebrisRESTTemplate RunDebris() {
        Jedis jedis = null;
        try {
            //查询所有并发
            int aspect_concurrency = 0;
            int slope_concurrency = 0;
            int filter_concurrency = 0;
            int classification_concurrency = 0;
            int debris_concurrency = 0;
            int data_concurrency = 0;

//        redis操作
            jedis = new Jedis(redisHost); // 默认端口为6379
            // 执行Redis命令
            long incrementedValue = jedis.incr("debris_concurrency");
            int concurrency = (int) incrementedValue;
            System.out.println("Incremented value: " + incrementedValue);
            System.out.println("当前并发: " + concurrency);

            debris_concurrency = concurrency;
            try{
                //            查询所有当前并发
                aspect_concurrency = Integer.parseInt(jedis.get("aspect_concurrency"));
                slope_concurrency = Integer.parseInt(jedis.get("slope_concurrency"));
                filter_concurrency = Integer.parseInt(jedis.get("filter_concurrency"));
                classification_concurrency = Integer.parseInt(jedis.get("classification_concurrency"));
//                debris_concurrency = Integer.parseInt(jedis.get("debris_concurrency"));
                data_concurrency = Integer.parseInt(jedis.get("data_concurrency"));
            }catch (Exception e){
                System.out.println(e);
            }

            Date date = new Date();
            SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");

            // 设置时区为中国时区
            TimeZone chinaTimeZone = TimeZone.getTimeZone("Asia/Shanghai");
            timeFormat.setTimeZone(chinaTimeZone);

            String currentTime = timeFormat.format(date);
            // System.out.println(currentTime);
            String today = currentTime.split("_")[0];
            long startTime = System.currentTimeMillis();// 开始运行时间
            if(req_timestamp<1e11){
                req_timestamp = req_timestamp*1000;
            }
            long Delay = Math.abs(startTime-req_timestamp); //网络延迟


            // 添加授权验证
            String auth = Base64.getEncoder().encodeToString(String.format("%s:%s", user, password).getBytes(StandardCharsets.UTF_8));
            String metaData = CommonUtil.MetaData(inputDEMURL, auth);
            String logLine = String.format("RequestUUID:%s;LogTime:%s;Status:Running;ClientName:%s;ClientIP:%s;ServerName:%s;ServerIP:%s;MetaData:%s;StartTime:%d\n", uuid, currentTime, clientName, clientIP, physicalName, physicalIP, metaData, startTime);

            int flag = 0;
            // 记录日志：初始状态
            String logFilePath = String.format("%s/Debris%s.log", logDir, today);
            File fLog = new File(logFilePath);
            if (!fLog.exists()) {
                System.out.println("log file not found! try to create...");
                try {
                    fLog.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("log file found! append to the end...");
            }
            try {
                BufferedWriter logWriter = new BufferedWriter(new FileWriter(fLog, true));
                logWriter.write(logLine);
                logWriter.flush();
                logWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            // 文件下载
            // 从inputRasterURL中解析文件名称
            // 文件下载
            // 从inputRasterURL中解析文件名称
            // TODO 数据日志服务
            String demDataPath = String.format("%s/download/%s", cacheDir, parseDataName(inputDEMURL));
            String filterDataPath = String.format("%s/download/%s", cacheDir, parseDataName(inputFilterURL));
            // TODO cache文件夹缓存清理设置
            // TODO 数据日志服务：第一次调用（初始状态）
            boolean isDEM = downloadData(inputDEMURL, demDataPath, auth);
            boolean isFilter = downloadData(inputFilterURL, filterDataPath, auth);
            // TODO 数据日志服务：第二次调用（数据获取状态）
            String storeName = String.format("Debris%s", uuid);
            String resultPath = String.format("%s/result/%s.tif", cacheDir, storeName);
            if ((!isDEM) || (!isFilter)) {
                // dem和filter有一个下载失败
                flag = 0;
            }

            //            python服务处理
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<String> future = null;
            // 创建一个Callable任务
            Callable<String> task = () -> {
                // 坡度处理 并发问题
                try {
                    Process process;

                    String command = String.format("%s %s %s %s %s", pythonInterpreter, scriptPath, demDataPath, filterDataPath, resultPath);
                    process = new ProcessBuilder(command.split(" ")).start();
                    int exitCode;
                    try {
                        exitCode = process.waitFor();
                        if (exitCode == 0) {
                            System.out.println("程序执行成功！");
                        } else {
                            System.err.println("程序执行出错：exit=" + exitCode);
                        }
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                } catch (Exception ioe) {
                    System.out.println(ioe);
                }
                return "Task completed successfully!";
            };

            try {
                // 提交任务并获取Future对象
                future = executor.submit(task);
                // 设置最大等待时间为10秒
                String result = future.get(10, TimeUnit.SECONDS);

                // 任务在规定时间内完成
                System.out.println(result);
            } catch (TimeoutException e) {
                // 任务超时，取消任务
                System.out.println("Task timed out! Cancelling...");
                future.cancel(true);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            } finally {
                // 关闭线程池
                executor.shutdown();
            }

            // 上传处理结果到geoserver中
            String outputRasterURL = "";
            try {
                // 检查工作区是否存在
                URL geoserver = new URL(outputGeoserverURL);
                GeoServerRESTManager manager = new GeoServerRESTManager(geoserver, user, password);
                GeoServerRESTPublisher publisher = manager.getPublisher();
                List<String> workspaces = manager.getReader().getWorkspaceNames();
                if (!workspaces.contains(workspace)) {
                    boolean whetherWS = publisher.createWorkspace(workspace);
                    System.out.printf("create workspace : %b%n", whetherWS);
                } else {
                    System.out.printf("workspace(%s) existed!%n", workspace);
                }
                // 上传数据……
                GSGeoTIFFDatastoreEncoder encoder = new GSGeoTIFFDatastoreEncoder(storeName);
                encoder.setWorkspaceName(workspace);
                encoder.setUrl(new URL(String.format("file:%s", resultPath)));
                boolean whetherStore = manager.getStoreManager().create(workspace, encoder);
                System.out.printf("create datastore : %b%n", whetherStore);

    //            多线程时，中断约1min
                boolean whetherPublish = manager.getPublisher().publishGeoTIFF(workspace, storeName, new File(resultPath));

                System.out.printf("publish : %b%n", whetherPublish);
                outputRasterURL = String.format("%sows?service=WCS&version=2.0.1&request=GetCoverage&coverageId=%s:%s&format=image/tiff", outputGeoserverURL, workspace, storeName);
                if (whetherPublish) {
                    flag = 1;
                } else {
                    flag = 0;
                }
            } catch (Exception e) {
                e.printStackTrace();
                flag = 0;
            }

            // 日志设置
            // 程序执行时间
            long endTime = System.currentTimeMillis();
            long responseTime = endTime - startTime;//程序响应时间
            System.out.printf(String.format("网络延迟：%dms%n", Delay));
            System.out.printf(String.format("程序响应时间：%dms%n", responseTime));

            //计算可靠性
            //读取txt文件
            int count = 0;
            int count1 = 0;
            double reliability0 = 0.5;
            try {
                String encoding="UTF-8";
                File file=new File(reliabilityTxt);
                if(file.isFile() && file.exists()){ //判断文件是否存在
                    InputStreamReader read = new InputStreamReader(
                            new FileInputStream(file),encoding);//考虑到编码格式
                    BufferedReader bufferedReader = new BufferedReader(read);
                    String lineTxt = null;
                    List<Integer> flagArray = new ArrayList<>();
                    while((lineTxt = bufferedReader.readLine()) != null){
                        int tmpFlag = (lineTxt.split(",")[1].equals("1"))?1:0;
                        flagArray.add(tmpFlag);
                        count++;
                    }
                    if (count>100){
                        for (int i=0;i<100;i++){
                            count1+=flagArray.get(i+count-100);
                        }
                        reliability = reliability0+(count1*0.005)-(100-count1)*0.005;
                    }else {
                        for (Integer integer : flagArray) {
                            count1 += integer;
                        }
                        reliability = reliability0+(count1*0.005)-(count-count1)*0.005;
                    }
                    read.close();
                }else{
                    System.out.println("找不到指定的文件");
                }
            } catch (Exception e) {
                System.out.println("读取文件内容出错");
                e.printStackTrace();
            }

            //计算可用性
            //读取txt文件
    //        int count = 0;
    //        int count1 = 0;
            try {
                String encoding="UTF-8";
                File file=new File(availabilityTxt);
                if(file.isFile() && file.exists()){ //判断文件是否存在
                    InputStreamReader read = new InputStreamReader(
                            new FileInputStream(file),encoding);//考虑到编码格式
                    BufferedReader bufferedReader = new BufferedReader(read);
                    String lineTxt = null;

                    List<String> AvaiArray = new ArrayList<>();
                    while((lineTxt = bufferedReader.readLine()) != null){
                        AvaiArray.add(lineTxt);
                    }
                    int Turn = AvaiArray.size();
                    java.util.Date date1=timeFormat.parse(AvaiArray.get(0).split(",")[0]);
                    java.util.Date date2=timeFormat.parse(AvaiArray.get(Turn-1).split(",")[0]);
                    long TimeDf = date2.getTime()-date1.getTime();
                    int Sumturn = (int)(TimeDf/(1000*60));
                    availability = (double)(Turn-1)/(double)(Sumturn);

    //                Date date1 = new Date(AvaiArray.get(1).split(",")[0]);
                    read.close();
                }else{
                    System.out.println("找不到指定的文件");
                }
            } catch (Exception e) {
                System.out.println("读取文件内容出错");
                e.printStackTrace();
            }

            //将记录写入txt文件
            String content = currentTime+","+flag;
            BufferedWriter out = null;
            try {
                out = new BufferedWriter(new OutputStreamWriter(
                        new FileOutputStream(reliabilityTxt, true)));
                out.write(content+"\r\n");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            Random random=new Random();
            price=random.nextInt(4)+5;
            System.out.println(String.format("reliability:%f availability:%f price:%f", reliability, availability, price));

            String finishedLogLine = logLine.substring(0, logLine.length() - 1) + String.format
                    (";EndTime:%d;Succeed:%d;ResponseTime:%d;Delay:%d;Availability:%f;Reliability:%f;price:%f;" +
                                    "aspect_concurrency:%d;slope_concurrency:%d;filter_concurrency:%d;classification_concurrency:%d;debris_concurrency:%d;data_concurrency:%d\n",
                            endTime, flag, responseTime, Delay, availability, reliability, price,
                            aspect_concurrency,slope_concurrency,filter_concurrency,classification_concurrency,debris_concurrency,data_concurrency);


            if (flag == 0) {
                finishedLogLine = finishedLogLine.replaceAll("Status:Running", "Status:Error");
            } else {
                finishedLogLine = finishedLogLine.replaceAll("Status:Running", "Status:Completed");
            }
            // 更新日志文件
            boolean whetherLog = CommonUtil.ReplaceLogByUUID(logFilePath, uuid, finishedLogLine);
            return new DebrisRESTTemplate(outputRasterURL, finishedLogLine, whetherLog);

        }catch (Exception e){
            System.out.println(e);
        }finally {
            //        redis操作
            jedis = new Jedis(redisHost); // 默认端口为6379
            // 执行Redis命令
            long decrementedValue = jedis.decr("debris_concurrency");
            System.out.println("decrementedValue value: " + decrementedValue);
            // 关闭连接
            jedis.close();
            System.out.println("断点00000009");
        }
        return null;
    }
}
